#include <STM32FreeRTOS.h>
#include "plc_debug.h"
#include "hwboard.h"
#include "plc_app.h"
#include "plc_abi.h"
#define XP1 PG1
#define XP2 PE8
#define XP3 PE12
#define XP4 PE14
#define XP5 PE7
#define XP6 PG0
#define XP7 PD14
#define XP8 PG2
#define XP9 PG4
#define XP10 PG6
#define XP11 PD15
#define XP12 PG3

#define YP1 PA5
#define YP2 PA6
#define YP3 PE9
#define YP4 PD12
#define YP5 PC6
#define YP6 PE5
#define YP7 PF6
#define YP8 PF7
#define YP9 PF8
#define YP10 PF9
#define YP11 PA0
#define YP12 PE11
#define YP13 PD13
#define YP14 PB0
#define YP15 PB1
#define YP16 PE13
#define YP17 PF11
#define YP18 PF12
#define YP19 PF13
#define YP20 PF14

#include <pt100_hx711.h>
pt100_hx711 hx1(PF2, PC15), hx2(PF10, PF5), hx3(PA4, PC3), hx4(PC14, PC13), hx5(PF4, PF3), hx6(PC2, PC0), hx7(PE0, PB9), hx8(PE4, PE3), hx9(PB8, PB7), hx10(PE2, PE1);

#include "swspi.h"
#include "ltc2496.h"
SWSPI spi(PD9, PD10, PD11, PD8, 1, 0);
ltc2496 ad;

#include <Wire.h>
TwoWire i2c(PF0, PF1);
ch423 ch(&i2c);
#include "Rtc_Pcf8563.h"
Rtc_Pcf8563 pcf8563(&i2c);

#include <MCPDAC.h>
MCPDACx4 mcp;

#include <RA165.h>
#define DI_SH PC9
#define DI_SK PE6
#define DI_QH PC8

RA165 ra165(DI_SK, DI_QH);

#include <ShiftRegister74HC595.h>
ShiftRegister74HC595 sr(3, PA15, PD3, PD1);
#include "debouncer.h"
debouncer *dx5, *dx8;

#include "SPI.h"
SPIClass fspi(PB15, PB14, PB10);

#ifdef LFS
FlashFs flash_fs;
#endif
#include "retain_flash.h"
retain_flash cretain;
retain_flash cfg_retain;
// SPIFlash *cflash;
const uint8_t pin_beep = PB3;
#ifdef USE_USBH
#include "usbstick.h"
USBStick usb;
#endif
#include <STM32Ethernet.h>
#include <lwip/apps/tftp_server.h>
#include <IWatchdog.h>
void httpd_setup();
void httpd_loop();
#include "ppp_class.h"
ppp_class ppp;

#include "AccelStepper.h"
AccelStepper as[] = {AccelStepper(TIM2, YP1, YP11), AccelStepper(TIM3, YP2, YP12), AccelStepper(TIM1, YP3, YP13), AccelStepper(TIM4, YP4, YP14), AccelStepper(TIM8, YP5, YP15), AccelStepper(TIM9, YP6, YP16), AccelStepper(TIM10, YP7, YP17), AccelStepper(TIM11, YP8, YP18), AccelStepper(TIM13, YP9, YP19), AccelStepper(TIM14, YP10, YP20)};
#ifdef PLC
extern "C" int __init();
#endif
uint32_t net_tick;
hwboard::hwboard()
{
    extern int VECTOR_ISR;
    SCB->VTOR = (uint32_t)&VECTOR_ISR;
    ipcfg.ip1 = DEFAULT_IP1;
    ipcfg.ip2 = DEFAULT_IP2;
    ipcfg.ip3 = DEFAULT_IP3;
    ipcfg.ip4 = DEFAULT_IP4;
    status = 1;
    toInit = 0;
    reboot = 0;
}
void hwboard::baseinit()
{
    pinMode(pin_beep, OUTPUT);
    digitalWrite(pin_beep, HIGH);

    pinMode(LED_RUN, OUTPUT);
    pinMode(LED_ERR, OUTPUT);
    pinMode(LED_ETH, OUTPUT);
    digitalWrite(LED_ERR, HIGH);
    digitalWrite(LED_RUN, HIGH);
    digitalWrite(LED_ETH, HIGH);

    delay(10);
    i2c.begin(MASTER_ADDRESS);
    delay(10);
    ch.begin();
    plc_set_state(Booting);
// pcf8563.initClock();

// ch423_test();
#ifdef LFS
    flash_fs.begin(&fspi, PA15);
    flash_fs.dir();
#else
    cflash = new SPIFlash(&fspi, PC7);
    cflash->initialize();
#endif
}
extern "C"
{
    void eth_reset()
    {
        //ETH RESET
        pinMode(PE10, OUTPUT);
        digitalWrite(PE10, LOW);
        delay(10);
        digitalWrite(PE10, HIGH);
        delay(10);
    }
}
void hwboard::beep(bool onoff)
{
    if (onoff)
        digitalWrite(pin_beep, HIGH);
    else
        digitalWrite(pin_beep, LOW);
}

void hwboard::netInit()
{

    load_config();
    beep(0);
    eth_reset();
#if DHCP
    if (1)
        Ethernet.begin();
    else
#endif
    {
        core_debug("IP: %d.%d.%d.%d\n", ipcfg.ip1, ipcfg.ip2, ipcfg.ip3, ipcfg.ip4);
        IPAddress ipp(ipcfg.ip1, ipcfg.ip2, ipcfg.ip3, ipcfg.ip4);
        Ethernet.begin(ipp);
        core_debug("net begined.\n");
    }
    net_tick = millis();
#ifdef HTTPD
    httpd_setup();
#endif
// #ifdef TFTPD
//     extern struct tftp_context tftp_ctx;
//     tftp_init(&tftp_ctx);
// #endif
#ifdef PLC
#ifdef ONLINE_DEBUG
    // plc_debug_init();
#endif
#endif
#ifdef USE_USBH
    core_debug("usb begin");
    usb.begin();
    core_debug("usb begined");
#endif

    io_init();

    atemp_init = analogRead(ATEMP);
    hx1.begin();
    hx2.begin();
    hx3.begin();
    hx4.begin();
    hx5.begin();
    hx6.begin();
    hx7.begin();
    hx8.begin();
    hx9.begin();
    hx10.begin();
}
void hwboard::begin()
{

    enOut = 1;
    enInput = 1;
};
void hwboard::io_init()
{
    pinMode(PD0, OUTPUT); //OE595
    digitalWrite(PD0, LOW);
    sr.begin();
    sr.setAllLow();

    mcp.begin(PB6, PB4, PG11, PG13, PG10, PG12, PG15);
    mcp.setGain(0, true);
    mcp.setGain(1, true);
    mcp.shutdown(0, false);
    mcp.shutdown(1, false);

    dx5 = new debouncer(4095);
    dx8 = new debouncer(4095);
    ad.begin(&spi, PD11);
    wdg_en = 0;
    wdg_time = 2000;
    pinMode(XP1, INPUT);
    pinMode(XP2, INPUT);
    pinMode(XP3, INPUT);
    pinMode(XP4, INPUT);
    pinMode(XP5, INPUT);
    pinMode(XP6, INPUT);
    pinMode(XP7, INPUT);
    pinMode(XP8, INPUT);
    pinMode(XP9, INPUT);
    pinMode(XP10, INPUT);
    pinMode(XP11, INPUT);
    pinMode(XP12, INPUT);

    pinMode(YP1, OUTPUT);
    pinMode(YP2, OUTPUT);
    pinMode(YP3, OUTPUT);
    pinMode(YP4, OUTPUT);
    pinMode(YP5, OUTPUT);
    pinMode(YP6, OUTPUT);
    pinMode(YP7, OUTPUT);
    pinMode(YP8, OUTPUT);
    pinMode(YP9, OUTPUT);
    pinMode(YP10, OUTPUT);
    pinMode(YP11, OUTPUT);
    pinMode(YP12, OUTPUT);
    pinMode(YP13, OUTPUT);
    pinMode(YP14, OUTPUT);
    pinMode(YP15, OUTPUT);
    pinMode(YP16, OUTPUT);
    pinMode(YP17, OUTPUT);
    pinMode(YP18, OUTPUT);
    pinMode(YP19, OUTPUT);
    pinMode(YP20, OUTPUT);
    for (int i = 0; i < 10; i++)
    {
        as[i].begin();
    }
}
extern const unsigned short build_number;
extern "C" size_t xPortGetFreeHeapSize(void);
char hwboard::get_din(int ch)
{
    if (ch >= 0 && ch < 24)
    {
        return xx[ch / 8] & (1 << (ch % 8)) ? 0 : 1;
    }
    return 0;
}
char hwboard::get_dout(int ch)
{
    if (ch >= 0 && ch < 24)
    {
        return yy[ch / 8] & (1 << (ch % 8)) ? 0 : 1;
    }
    return 0;
}
int hwboard::get_ain(int ch, void *val)
{
    if (ch >= 0 && ch < 10)
    {
        *(uint16_t *)val = *(xa + ch);
        return *(uint16_t *)val;
    }
    return -1;
}
char hwboard::get_dbg_jmpr_get()
{
    return digitalRead(PA9) ? 0 : 1;
}
char hwboard::get_rst_jmpr_get()
{
    return digitalRead(PA10) ? 0 : 1;
}
void hwboard::rtc_get()
{
    pcf8563.getDateTime();
    year = pcf8563.getYear() + (pcf8563.getCentury() ? 1900 : 2000);
    month = pcf8563.getMonth();
    day = pcf8563.getDay();
    week = pcf8563.getWeekday();
    hour = pcf8563.getHour();
    minute = pcf8563.getMinute();
    second = pcf8563.getSecond();
}
void hwboard::board_input()
{
    pinMode(DI_SH, OUTPUT);
    digitalWrite(DI_SH, LOW);
    for (int i = 0; i < 10; i++)
        xa[i] = (ad.read(i));
    digitalWrite(DI_SH, HIGH);
    xx[2] = ~ra165.readByte(MSB, 1);
    xx[1] = ~ra165.readByte(MSB, 0);
    xx[0] = ~ra165.readByte(MSB, 0);
    xp[2] = ~ra165.readByte(MSB, 0);
    xp[0] = xp[1] = 0;
    xp[0] |= digitalRead(XP1) << 0;
    xp[0] |= digitalRead(XP2) << 1;
    xp[0] |= digitalRead(XP3) << 2;
    xp[0] |= digitalRead(XP4) << 3;
    xp[0] |= digitalRead(XP5) << 4;
    xp[0] |= digitalRead(XP6) << 5;
    xp[0] |= digitalRead(XP7) << 6;
    xp[0] |= digitalRead(XP8) << 7;
    xp[1] |= digitalRead(XP9) << 0;
    xp[1] |= digitalRead(XP10) << 1;
    xp[1] |= digitalRead(XP11) << 2;
    xp[1] |= digitalRead(XP12) << 3;

    hx1.loop();
    hx2.loop();
    hx3.loop();
    hx4.loop();
    hx5.loop();
    hx6.loop();
    hx7.loop();
    hx8.loop();
    hx9.loop();
    hx10.loop();
    xt[0] = hx1.rvalue();
    xt[1] = hx2.rvalue();
    xt[2] = hx3.rvalue();
    xt[3] = hx4.rvalue();
    xt[4] = hx5.rvalue();
    xt[5] = hx6.rvalue();
    xt[6] = hx7.rvalue();
    xt[7] = hx8.rvalue();
    xt[8] = hx9.rvalue();
    xt[9] = hx10.rvalue();
    static long last_tick;
    if (millis() - last_tick >= 1000)
    {
        rtc_get();
        last_tick = millis();
    }
    mem_remain = xPortGetFreeHeapSize();
}
void hwboard::ioled()
{
    switch (plc_state)
    {
    case Broken:
        ch.setChar('B', 2);
        break;
    case NoPLC:
        ch.setChar('N', 2);
        break;
    case Disconnected:
        ch.setChar('d', 2);
        break;
    case Empty:
        ch.setChar('M', 2);
        break;
    case Ymodem:
        ch.setChar('Y', 2);
        break;
    case Tfttd:
        ch.setChar('T', 2);
        break;
    case Booting:
        ch.setChar('b', 1);
        break;
    case PLC_ERROR:
        ch.setChar('E', 2);
        break;
    case Restart:
        ch.setChar('r', 2);
        break;
    case FLASH_FORMAT:
        ch.setChar('F', 3);
        break;
    case USB_INSERTED:
        ch.setChar('U', 3);
        break;
    case USB_COPYING:
        ch.setChar('C', 3);
        break;
    case USB_COPYED:
        ch.setChar('D', 1);
        break;
    case USB_NO_FILE:
        ch.setChar('n', 2);
        break;

    case APP_FLASH_END:
        ch.setChar('E', 3);
        break;
    case APP_FLASHING:
        ch.setChar('W', 1);
        break;
    case APP_FLASH_BEGIN:
        ch.setChar('P', 1);
        break;
    case Stopped:
        ch.setChar('P', 2);
        break;
    case Started:

        for (int i = 0; i < 24; i++)
        {
            char v = (xp[i / 8] & (1 << (i % 8))) ? 1 : 0;
            ch.setBit(i / 8, i % 8, v);
        }

        for (int i = 0; i < 24; i++)
        {
            char v = (yy[i / 8] & (1 << (i % 8))) ? 1 : 0;
            ch.setBit(i / 8 + 3, i % 8, v);
        }
        for (int i = 0; i < 8; i++)
        {
            char v = alarm & (1 << i);
            ch.setBit(6, i, v ? 2 : 0);
        }
        for (int i = 0; i < 8; i++)
        {
            char v = status & (1 << i);
            ch.setBit(7, i, v ? 3 : 0);
        }
        break;
    default:
        break;
    }
    ch.update();
}
void hwboard::set_dout(int ch, char val)
{
    if (ch >= 0 && ch < 24)
    {
        u8 b = ch / 8;
        if (val)
            yy[b] |= 1 << (ch % 8);
        else
            yy[b] &= ~(1 << (ch % 8));
        sr.setNoUpdate(ch, val);
    }
}
int hwboard::get(int l1, int l2, void *val)
{
    // assert(l1>0&&l1<7);
    switch (l1)
    {
    case 1:
        *(char *)val = get_din(l2 - 1);
        return *(char *)val;
    case 2:
        *(char *)val = get_dout(l2 - 1);
        return *(char *)val;

    case 3:
        *(uint16_t *)val = get_ain(l2 - 1, val);
        return *(uint16_t *)val;
    case 4:
        if (l2 > 0 && l2 <= 4)
        {
            *(float *)val = *(&xt[1] + l2 - 1);
            return *(float *)val;
        }
        else
        {
            *(uint16_t *)val = *(&xt[1] + l2 - 5);
            return *(uint16_t *)val;
        }
        break;
    case 5:
        if (l2 == 2)
            *(uint16_t *)val = ya[1];
        else
            *(uint16_t *)val = ya[2];
        return 0;
    case 6:
        switch (l2)
        {
        case 1:
            *(uint16_t *)val = year;
            return 0;
        case 2:
            *(uint8_t *)val = month;
            return 0;
        case 3:
            *(uint8_t *)val = day;
            return 0;
        case 4:
            *(uint8_t *)val = hour;
            return 0;
        case 5:
            *(uint8_t *)val = minute;
            return 0;
        case 6:
            *(uint8_t *)val = second;
            return 0;
        case 7:
            *(uint8_t *)val = week;
            return 0;
        case 8:
            *(uint8_t *)val = rtc_write;
            return 0;
        case 9:
            *(uint8_t *)val = wdg_en;
            return 0;
        case 10:
            *(uint16_t *)val = wdg_time;
            return 0;
        case 11:
            *(uint8_t *)val = reboot;
            return 0;
        case 12:
            *(uint32_t *)val = millis();
            return 0;
        case 13:
            *(uint32_t *)val = millis();
            return 0;
        case 14:
            *(uint8_t *)val = status;
            return 0;
        case 15:
            *(uint8_t *)val = alarm;
            return 0;
        case 16:
            *(uint32_t *)val = xPortGetFreeHeapSize();
            return 0;
        case 17:
            *(uint8_t *)val = toInit;
            return 0;
        case 18:
            *(uint8_t *)val = enUSBHost;
            return 0;
        case 19:
            *(uint8_t *)val = ipcfg.ip1;
            return 0;
        case 20:
            *(uint8_t *)val = ipcfg.ip2;
            return 0;
        case 21:
            *(uint8_t *)val = ipcfg.ip3;
            return 0;
        case 22:
            *(uint8_t *)val = ipcfg.ip4;
            return 0;
        case 23:
            *(uint8_t *)val = ipcfg.mask1;
            return 0;
        case 24:
            *(uint8_t *)val = ipcfg.mask2;
            return 0;
        case 25:
            *(uint8_t *)val = ipcfg.mask3;
            return 0;
        case 26:
            *(uint8_t *)val = ipcfg.mask4;
            return 0;
        case 27:
            *(uint8_t *)val = ipcfg.gate1;
            return 0;
        case 28:
            *(uint8_t *)val = ipcfg.gate2;
            return 0;
        case 29:
            *(uint8_t *)val = ipcfg.gate3;
            return 0;
        case 30:
            *(uint8_t *)val = ipcfg.gate4;
            return 0;
        case 31:
            *(uint8_t *)val = ipSet;
            return 0;
        default:
            core_debug("io get:unHandle %d.%d\n", l1, l2);
            return -1;
        }
    default:
        core_debug("io get: unHandle %d.%d\n", l1, l2);
        return -2;
    }
}
void hwboard::set_sys(int l2, void *val)
{
    switch (l2)
    {
    case 1:
        wyear = *(uint16_t *)val;
        break;
    case 2:
        wmonth = *(uint8_t *)val;
        break;
    case 3:
        wday = *(uint8_t *)val;
        break;
    case 4:
        whour = *(uint8_t *)val;
        break;
    case 5:
        wminute = *(uint8_t *)val;
        break;
    case 6:
        wsecond = *(uint8_t *)val;
        break;
    case 7:
        week = *(uint8_t *)val;
        break;
    case 8:
        rtc_write = *(uint8_t *)val;
        break;
    case 9:
        wdg_en = *(uint8_t *)val;
        break;
    case 10:
        wdg_time = *(uint16_t *)val;
        break;
    case 11:
        reboot = *(uint8_t *)val;
        break;
    case 14:
        status = *(uint8_t *)val;
        break;
    case 15:
        alarm = *(uint8_t *)val;
        break;
    case 17:
        toInit = *(uint8_t *)val;
        if (toInit)
        {
            initReset();
            toInit = 0;
        }
        break;
    case 18:
#ifdef USE_USBH

// if (*(uint8_t *)val)
// {
//     usb.begin();
// }
// else
//     usb.stop();
#endif
        break;
    case 19:
        ipcfg.ip1 = *(uint8_t *)val;
        break;
    case 20:
        ipcfg.ip2 = *(uint8_t *)val;
        break;
    case 21:
        ipcfg.ip3 = *(uint8_t *)val;
        break;
    case 22:
        ipcfg.ip4 = *(uint8_t *)val;
        break;
    case 23:
        ipcfg.mask1 = *(uint8_t *)val;
        break;
    case 24:
        ipcfg.mask2 = *(uint8_t *)val;
        break;
    case 25:
        ipcfg.mask3 = *(uint8_t *)val;
        break;
    case 26:
        ipcfg.mask4 = *(uint8_t *)val;
        break;
    case 27:
        ipcfg.gate1 = *(uint8_t *)val;
        break;
    case 28:
        ipcfg.gate2 = *(uint8_t *)val;
        break;
    case 29:
        ipcfg.gate3 = *(uint8_t *)val;
        break;
    case 30:
        ipcfg.gate4 = *(uint8_t *)val;
        break;
    case 31:
        if (!ipSet)
            ipSet = *(uint8_t *)val;
        break;
    default:
        break;
    }
}
void hwboard::set(int l1, int l2, void *val)
{
    switch (l1)
    {
    case 2:
        set_dout(l2 - 1, *(char *)val);
        break;
    case 5:
        if (l2 == 2)
            ya[1] = *(uint16_t *)val;
        else
            ya[2] = *(uint16_t *)val;
        mcp.setVoltage(l2 == 1 ? 1 : 0, *(uint16_t *)val);
        break;
    case 6:
        set_sys(l2, val);
        break;
    default:
        while (1)
            ;
    }
}
void hwboard::rtc_setup()
{
    pcf8563.setDateTime(wday, 1, wmonth, 0, wyear - 2000, whour, wminute, wsecond);
    rtc_write = 0;
}
u8 mcpTable[] = {4, 0, 6, 2, 5, 1, 7, 3};
void hwboard::board_output()
{
    yy[0] = xx[0];
    yy[1] = xx[1];
    yy[2] = xx[2];
    u8 vx = xp[0] & 1;
    digitalWrite(YP1, vx);
    digitalWrite(YP2, vx);
    digitalWrite(YP3, vx);
    digitalWrite(YP4, vx);
    digitalWrite(YP5, vx);
    digitalWrite(YP6, vx);
    digitalWrite(YP7, vx);
    digitalWrite(YP8, vx);
    digitalWrite(YP9, vx);
    digitalWrite(YP10, vx);
    digitalWrite(YP11, vx);
    digitalWrite(YP12, vx);
    digitalWrite(YP13, vx);
    digitalWrite(YP14, vx);
    digitalWrite(YP15, vx);
    digitalWrite(YP16, vx);
    digitalWrite(YP17, vx);
    digitalWrite(YP18, vx);
    digitalWrite(YP19, vx);
    digitalWrite(YP20, vx);
    for (int i = 0; i < 24; i++)
    {
        char v = (yy[i / 8] & (1 << (i % 8))) ? 1 : 0;
        sr.setNoUpdate(i, v);
    }

    sr.updateRegisters();
    for (int i = 0; i < 8; i++)
    {
        ya[i] = i * 500 + 500;
        mcp.setVoltage(mcpTable[i], ya[i]);
    }
    // mcp.update();
    if (rtc_write)
    {
        rtc_setup();
    }
    if (wdg_en)
    {
        if (!IWatchdog.isEnabled())
        {
            IWatchdog.begin(wdg_time * 1000);
        }
    }
    if (ipSet)
    {
        if (ipSet++ == 2)
        {
            save_config();
            IPAddress ipp(ipcfg.ip1, ipcfg.ip2, ipcfg.ip3, ipcfg.ip4);
            Ethernet.set_ip(ipp);
            ipSet = 0;
        }
    }
    if (reboot)
    {
        if (reboot++ == 2)
        {
            reboot = 0;
            setReboot();
        }
    }
    // if (enUSBHost)
    // usb.begin();
    // else
    // usb.stop();
}
void plc_loop_cb()
{
    IWatchdog.reload();
}
void hwboard::initReset()
{
#ifdef PLC
    board.retain_reset();
    __init();
#endif
    // plc_app_stop();
    // board.retain_reset();
    plc_curr_app->config_init();
    // plc_app_start();
}
void plc_app_stop();
void hwboard::flashClear()
{
    plc_app_stop();
    plc_set_state(FLASH_FORMAT);
#ifdef LFS
    flash_fs.clear();
    vTaskDelay(10);
    flash_fs.format();
#else
    cflash->chipErase();
#endif
    vTaskDelay(10);
    setReboot();
    plc_set_state(Restart);
    while (1)
        ;
}
hwboard board;

int test_index;
extern "C" void OutputIndex(int line, const char *msg)
{
    test_index++;
    ch.setChar('0' + test_index % 10, 2);
    digitalWrite(LED_ERR, HIGH);
    digitalWrite(LED_RUN, HIGH);
    digitalWrite(LED_ETH, HIGH);
    switch (test_index / 10)
    {
    case 0:
        break;
    case 1:
        digitalWrite(LED_RUN, LOW);
        break;
    case 2:
        digitalWrite(LED_ETH, LOW);
        break;
    case 3:
        digitalWrite(LED_ERR, LOW);
        break;
    default:
        digitalWrite(LED_ERR, LOW);
        digitalWrite(LED_RUN, LOW);
        digitalWrite(LED_ETH, LOW);
        break;
    }
    core_debug("\nTEST %d : %s , L%d\n", test_index, msg, line);
}
const char *hwModel = HWMODEL;
uint32_t last_led_tick;
void plc_run_start()
{
    if (digitalRead(LED_RUN) && (millis() - last_led_tick) > 200)
    {
        digitalWrite(LED_RUN, LOW);
        last_led_tick = millis();
    }
}
void plc_run_end()
{
    mcp.update();

    sr.updateRegisters();
    board.ioled();
    board.output();

    // if (!digitalRead(LED_RUN) && (millis() - last_led_tick) > 200)
    // {
    digitalWrite(LED_RUN, HIGH);
    //     last_led_tick = millis();
    // }
}
extern "C"
{
    void net_led_on()
    {
        digitalWrite(LED_ETH, LOW);
        Ethernet.touch();
    }
    void net_led_off()
    {
        digitalWrite(LED_ETH, HIGH);
        Ethernet.touch();
    }
}

void format()
{
    board.flashClear();
}

void hwboard::load_config()
{
#ifdef LFS
    boardConfig config;
    int buflen;
    if (FlashFile::stat("boardConfig") > 0)
    {
        FlashFile md;
        md.open_rdonly("boardConfig");
        buflen = md.read((char *)&config, sizeof(boardConfig));
        u32 ckm = GenerateCRC32Sum(&config, sizeof(boardConfig) - 4, 0);
        if (ckm == config.chksum)
        {
            ipcfg = config.ip;
        }
        md.close();
    }
#else
    cfg_retain.begin(cflash, sizeof(boardConfig), 4096 * 64, 4096 * 128, (const u8 *)plc_curr_app->id, 32, 0);
    if (cfg_retain.check())
    {
        cfg_retain.remind(0, sizeof(boardConfig), (void *)&cfg);
        ipcfg = cfg.ip;
    }
#endif
}
void hwboard::save_config()
{
#ifdef LFS
    FlashFile md;
    boardConfig config;
    config.ip = ipcfg;
    config.chksum = GenerateCRC32Sum(&config, sizeof(boardConfig) - 4, 0);
    md.open_write("boardConfig");
    md.write((const char *)&config, sizeof(boardConfig));
    md.close();
#else
    cfg.ip = ipcfg;
    cfg_retain.retain(0, sizeof(boardConfig), (void *)&cfg);
    cfg_retain._saveRetain(false);
#endif
}

void hwboard::reset_config()
{
#ifdef LFS
    flash_fs.unlink("boardConfig");
#else
    cfg_retain.reset();
#endif
}
#ifdef STM32F407xx
int8_t write_to_backup_sram(uint8_t *data, uint16_t bytes, uint16_t offset)
{
    const uint16_t backup_size = 0x1000;
    uint8_t *base_addr = (uint8_t *)BKPSRAM_BASE;
    uint16_t i;
    if (bytes + offset >= backup_size)
    {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
    __HAL_RCC_BKPSRAM_CLK_ENABLE();
    for (i = 0; i < bytes; i++)
    {
        *(base_addr + offset + i) = *(data + i);
    }
    __HAL_RCC_BKPSRAM_CLK_DISABLE();

    return 0;
}
int8_t read_from_backup_sram(uint8_t *data, uint16_t bytes, uint16_t offset)
{
    const uint16_t backup_size = 0x1000;
    uint8_t *base_addr = (uint8_t *)BKPSRAM_BASE;
    uint16_t i;
    if (bytes + offset >= backup_size)
    {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
    __HAL_RCC_BKPSRAM_CLK_ENABLE();
    for (i = 0; i < bytes; i++)
    {
        *(data + i) = *(base_addr + offset + i);
    }
    __HAL_RCC_BKPSRAM_CLK_DISABLE();
    return 0;
}
#endif
#if 0
int8_t write_to_backup_rtc(uint32_t *data, uint16_t bytes, uint16_t offset)
{
    const uint16_t backup_size = 80;
    volatile uint32_t *base_addr = &(RTC->BKP0R);
    uint16_t i;
    if (bytes + offset >= backup_size)
    {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
    else if (offset % 4 || bytes % 4)
    {
        /* ERROR: data start or num bytes are not word aligned */
        return -2;
    }
    else
    {
        bytes >>= 2; /* divide by 4 because writing words */
    }
    /* disable backup domain write protection */
    __HAL_RCC_PWR_CLK_ENABLE(); // set RCC->APB1ENR.pwren
    HAL_PWR_EnableBkUpAccess(); // set PWR->CR.dbp = 1;
    for (i = 0; i < bytes; i++)
    {
        *(base_addr + offset + i) = *(data + i);
    }
    HAL_PWR_DisableBkUpAccess(); // reset PWR->CR.dbp = 0;
    // consider also disabling the power peripherial?
    return 0;
}
int8_t read_from_backup_rtc(uint32_t *data, uint16_t bytes, uint16_t offset)
{
    const uint16_t backup_size = 80;
    volatile uint32_t *base_addr = &(RTC->BKP0R);
    uint16_t i;
    if (bytes + offset >= backup_size)
    {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
    else if (offset % 4 || bytes % 4)
    {
        /* ERROR: data start or num bytes are not word aligned */
        return -2;
    }
    else
    {
        bytes >>= 2; /* divide by 4 because writing words */
    }
    /* read should be 32 bit aligned */
    for (i = 0; i < bytes; i++)
    {
        *(data + i) = *(base_addr + offset + i);
    }
    return 0;
}
#endif
struct
{
    int v1;
    u8 sn[32];
    int v2;
} retain_t;
void hwboard::retain(unsigned int offset, unsigned int count, void *p)
{
#if 1
    cretain.retain(offset, count, p);
#else
#ifdef STM32F407xx
    if (enRetain && ((offset + count) < 4000))
    {
        write_to_backup_sram((uint8_t *)p, count, offset);
        int v1, v2;
        retain_t.v1 = retain_t.v2 = 0x55aa55aa;
        memcpy(retain_t.sn, plc_curr_app->id, 32);
        write_to_backup_sram((uint8_t *)&retain_t, sizeof(retain_t), 4000);
    }
#endif
#endif
}
void hwboard::retain_remind(unsigned int offset, unsigned int count, void *p)
{
#if 1
#ifdef LFS
    if (retain_buffer.opened())
    { /* Remind variable from file.  */
        retain_buffer.fseek(retain_info.header_offset + offset, SEEK_SET);
        retain_buffer.read((char *)p, count);
    }
#else
    cretain.remind(offset, count, p);
#endif
#else
#ifdef STM32F407xx
    if (enRetain && offset + count < 4000)
    {
        read_from_backup_sram((uint8_t *)p, count, offset);
    }
#endif
#endif
}

void hwboard::retain_reset()
{
#if 1
#ifdef LFS
    retain_buffer.unlink(rb_file);
    retain_buffer.unlink(rb_file_bckp);
#else
    cretain.reset();
#endif
#else
#ifdef STM32F407xx
    int v1;
    v1 = 55;
    write_to_backup_sram((uint8_t *)&v1, 4, 0);
    v1 = 0xaa;
    write_to_backup_sram((uint8_t *)&v1, 4, 4);
#endif
#endif
}
#include "hardware.h"
#include "config.h"
#ifdef STM32F407xx
#define BLOCK_SIZE 16384
#else
#define BLOCK_SIZE 2048
#endif
void app_flash_begin()
{
    int i = 0;
    FLASH_EraseInitTypeDef f;
#ifdef STM32F107xC
    HAL_FLASH_Unlock();
    f.TypeErase = FLASH_TYPEERASE_PAGES;
    f.PageAddress = USER_CODE_FLASH + BLOCK_SIZE * i;
    f.NbPages = 1;
#else
    UnProtectRWpages(FLASH_SECTOR_6);
    UnProtectRWpages(FLASH_SECTOR_7);
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR);
    f.TypeErase = FLASH_TYPEERASE_SECTORS;
    f.Banks = FLASH_BANK_1;
    f.Sector = FLASH_SECTOR_6;
    f.NbSectors = 2;
    f.VoltageRange = VOLTAGE_RANGE_3;
#endif
    //设置PageError
    uint32_t PageError = 0;
    //调用擦除函数
    uint32_t retf = HAL_FLASHEx_Erase(&f, &PageError);
    if ((retf != HAL_OK) || (PageError != 0xffffffff))
    {
        core_debug("erase error :0x%x\n", PageError);
    }
}
void hwboard::eraseapp()
{
    app_flash_begin();
    app_start = USER_CODE_FLASH;
}
void hwboard::setAppProtect()
{
}
void hwboard::writeApp(const char *buf, int len)
{
    for (int j = 0; j < len; j += 4)
    {
#ifdef STM32F407xx
        __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR);
#endif
        uint32_t ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, app_start + j, *(uint32_t *)(buf + j));
        switch (ret)
        {
        case HAL_OK:
            break;
        case HAL_TIMEOUT:
            core_debug("HAL_FLASH_Program() timeout!\n");
            break;
        case HAL_ERROR:
            core_debug("HAL_FLASH_Program() error 0x%08x, see *hal_flash.h for bit definitions\n", HAL_FLASH_GetError());
            break;
        default:
            core_debug("HAL_FLASH_Program() returned unknown status %lu\n", ret);
        }
    }

    app_start += len;
}

int hwboard::modbus_cb_read(u16 start_addr, u16 word_count, u16 *data_words)
{
    u32 id;
    for (int adr = start_addr; adr < start_addr + word_count; adr++)
    {
        switch (adr)
        {
        case 60000:
            *data_words = ipcfg.ip1;
            break;
        case 60001:
            *data_words = ipcfg.ip2;
            break;
        case 60002:
            *data_words = ipcfg.ip3;
            break;
        case 60003:
            *data_words = ipcfg.ip4;
            break;
        case 60004:
            *data_words = ipcfg.mask1;
            break;
        case 60005:
            *data_words = ipcfg.mask2;
            break;
        case 60006:
            *data_words = ipcfg.mask3;
            break;
        case 60007:
            *data_words = ipcfg.mask4;
            break;
        case 60008:
            *data_words = ipcfg.gate1;
            break;
        case 60009:
            *data_words = ipcfg.gate2;
            break;
        case 60010:
            *data_words = ipcfg.gate3;
            break;
        case 60011:
            *data_words = ipcfg.gate4;
            break;
        case 60012:
            *data_words = ipSet;
            break;
        case 60013:
            id = HAL_GetUIDw0();
            *data_words = id >> 16;
            break;
        case 60014:
            id = HAL_GetUIDw0();
            *data_words = id;
            break;
        case 60015:
            id = HAL_GetUIDw1();
            *data_words = id >> 16;
            break;
        case 60016:
            id = HAL_GetUIDw1();
            *data_words = id;
            break;
        case 60017:
            id = HAL_GetUIDw2();
            *data_words = id >> 16;
            break;

        case 60018:
            id = HAL_GetUIDw2();
            *data_words = id;
            break;
        case 60019:
            *data_words = millis() >> 16;
            break;
        case 60020:
            *data_words = millis();
            break;
        case 60021:
            *data_words = *(u16 *)hwModel;
            break;
        case 60022:
            *data_words = *(u16 *)&hwModel[2];
            break;
        case 60023:
            *data_words = *(u16 *)&hwModel[4];
            break;
        case 60024:
            *data_words = *(u16 *)&hwModel[6];
            break;
        case 60025:
            *data_words = reboot;
            break;
        case 60026:
            *data_words = xPortGetFreeHeapSize();
            break;
        default:
            return -2;
        }
        data_words++;
    }
    return 0;
}
int hwboard::modbus_cb_write(u16 start_addr, u16 word_count, u16 *data_words)
{
    for (int adr = start_addr; adr < start_addr + word_count; adr++)
    {
        switch (adr)
        {
        case 60000:
            ipcfg.ip1 = *data_words;
            break;
        case 60001:
            ipcfg.ip2 = *data_words;
            break;
        case 60002:
            ipcfg.ip3 = *data_words;
            break;
        case 60003:
            ipcfg.ip4 = *data_words;
            break;
        case 60004:
            ipcfg.mask1 = *data_words;
            break;
        case 60005:
            ipcfg.mask2 = *data_words;
            break;
        case 60006:
            ipcfg.mask3 = *data_words;
            break;
        case 60007:
            ipcfg.mask4 = *data_words;
            break;
        case 60008:
            ipcfg.gate1 = *data_words;
            break;
        case 60009:
            ipcfg.gate2 = *data_words;
            break;
        case 60010:
            ipcfg.gate3 = *data_words;
            break;
        case 60011:
            ipcfg.gate4 = *data_words;
            break;
        case 60012:
            ipSet = *data_words;
            break;
        case 60025:
            reboot = *data_words;
            break;
        default:
            return -2;
        }
        data_words++;
    }
    return word_count;
}
int sys_read_outwords(u16 start_addr, u16 word_count, u16 *data_words)
{
    return board.modbus_cb_read(start_addr, word_count, data_words);
}
int sys_write_outwords(u16 start_addr, u16 word_count, u16 *data_words)
{
    return board.modbus_cb_write(start_addr, word_count, data_words);
}
int hwboard::setip(set_ip_t *dat)
{
    ipcfg = *dat;
    ipSet = 1;
    save_config();
}
void *tftp_open(const char *fname, const char *mode, u8_t write)
{
    if (strncmp(fname, "plc.bin", 7) != 0)
        return 0;
    if (write)
    {
        board.eraseapp();
    }
    return (void *)fname;
}
char tftping;
void tftp_close(void *handle)
{
    IWatchdog.reload();
    board.setAppProtect();
    tftping = 0;
}
int tftp_read(void *handle, void *buf, int bytes)
{
    IWatchdog.reload();
    return 0;
    // board.readApp(buf,bytes)    ;
}
int tftp_write(void *handle, struct pbuf *p)
{
    IWatchdog.reload();
    board.writeApp((const char *)p->payload, p->len);
    return p->len;
}
struct tftp_context tftp_ctx
{
    tftp_open, tftp_close, tftp_read, tftp_write
};
int hwboard::start_tftpd()
{
    IWatchdog.begin(3 * 1000 * 1000);
    tftp_init(&tftp_ctx);
    tftping = 1;
    while (1)
    {
        vTaskDelay(1000);
        if (!tftping)
        {
            vTaskDelay(1000);
            tftp_close(&tftp_ctx);
            break;
        }
    }
}
void rxmodem_close()
{
    board.setAppProtect();
}
int rxmodem_open(char *filename)
{
    if (strncmp(filename, "plc.bin", 7) != 0)
    {
        board.eraseapp();
    }
    return 0;
}
int rxmodem_write(uint8_t *buf, int count)
{
    board.writeApp((const char *)buf, count);
}

#ifdef USE_USBH
#ifdef LFS
int copy_file(const char *name)
{

    plc_app_stop();
    plc_set_state(USB_INSERTED);

    char *buf = (char *)malloc(1024);
    int len;
    if (usb.open_file_rdonly(name) == 0)
    {
        plc_set_state(USB_COPYING);
        usb.stat(name);
        int s = usb.fsize;
        FlashFile b;
        b.open_write(name);

        while (s > 0)
        {
            len = usb.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            b.write(buf, len);
            s -= len;
        }
        b.close();
        usb.close();
        core_debug("copy ok");

        free(buf);
        plc_set_state(USB_COPYED);
    }
    else
    {
        plc_set_state(USB_NO_FILE);
    }
    return 0;
}
#endif
int copy_flash_app(const char *name)
{

    plc_app_stop();
    plc_set_state(USB_COPYING);

    char *buf = (char *)malloc(1024);
    int len;
    if (usb.open_file_rdonly(name) == 0)
    {
        plc_set_state(USB_COPYING);
        usb.stat(name);
        int s = usb.fsize;
        board.eraseapp();
        while (s > 0)
        {
            len = usb.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            board.writeApp(buf, len);
            s -= len;
        }
        board.setAppProtect();
        usb.close();
        core_debug("copy ok");

        free(buf);
        plc_set_state(USB_COPYED);
    }
    else
    {
        plc_set_state(USB_NO_FILE);
    }
    return 0;
}
int usb_to_flash(const char *name, u32 flash_start_addr, u32 header_size)
{
    u32 adr;
    bootloader_t ctl;
    adr = flash_start_addr + header_size;
    plc_app_stop();
    plc_set_state(USB_COPYING);

    char *buf = (char *)malloc(1024);
    int len;
    if (usb.open_file_rdonly(name) == 0)
    {
        plc_set_state(USB_COPYING);
        usb.stat(name);
        int s = usb.fsize;
        cflash->blockErase64K(adr);
        while (s > 0)
        {
            len = usb.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            cflash->writeBytes(adr, (const uint8_t *)buf, len);
            adr += len;
            s -= len;
        }
        board.setAppProtect();
        usb.close();
        core_debug("copy ok");
        ctl.len = usb.fsize;
        ctl.crc = 0;
        ctl.enable = 0x55aa55aa;
        cflash->writeBytes(flash_start_addr, (const uint8_t *)&ctl, sizeof(ctl));
        free(buf);
        plc_set_state(USB_COPYED);
    }
    else
    {
        plc_set_state(USB_NO_FILE);
    }
    return 0;
}
//char path[32];
void usb_ready()
{
    //    path[0] = 0;
    //    usb.ls(path);
    plc_set_state(Restart);
    plc_app_stop();
    setReboot();
}

void usb_disconnect()
{
    HAL_NVIC_SystemReset();
}
#endif

extern "C"
{
    void plc_set_state(u8 state)
    {
        plc_state = state;
        board.ioled();
    }
}
HardwareSerial Serial4(PC11, PC10);
HardwareSerial *Serials[] = {
    &Serial1,
    // &Serial2,
    // &Serial3,
    &Serial4,
    // &Serial5
};


extern "C" int GetRetainSize();

void ___saveRetain(void const *arg)
{
    uint32_t last_crc;
    vTaskDelay(1000);
    last_crc = GenerateCRC32Sum(retain_info.buf, retain_info.retain_size, retain_info.header_crc);
    while (1)
    {

        uint32_t crc = GenerateCRC32Sum(retain_info.buf, retain_info.retain_size, retain_info.header_crc);
        if (crc != last_crc)
        {
#ifdef LFS
            if (retain_buffer.opened())
            {
                retain_buffer.close();
            }
            flash_rename(rb_file, rb_file_bckp);

            /* Create new retain file.  */
            int a = retain_buffer.open_write(rb_file);
            if (a < 0)
            {
                core_debug("Failed to create retain file : %s\n", rb_file);
            }
            else
            {
                /* Write header to the new file.  */
                retain_buffer.write((char *)&retain_info.retain_size, sizeof(retain_info.retain_size));
                retain_buffer.write((char *)&retain_info.hash_size, sizeof(retain_info.hash_size));
                retain_buffer.write((char *)retain_info.hash, retain_info.hash_size);
                retain_buffer.write((char *)retain_info.buf, retain_info.retain_size);
                retain_buffer.write((char *)&crc, sizeof(uint32_t));
                last_crc = crc;
            }
            retain_buffer.close();
#else

#endif
        }
        vTaskDelay(3000);
    }
}
int hwboard::retain_close()
{
    cretain.close();
}
int hwboard::retain_check()
{
#if 1
#ifdef RTE_APP
    u32 retain_size = plc_curr_app->GetRetainSize();
#else
    u32 retain_size = GetRetainSize();
#endif
    cretain.begin(cflash, retain_size, 4096 * 192, 4096 * 256, (const u8 *)plc_curr_app->id, 32);
    return cretain.check();
#else
#ifdef STM32F407_xx
    __HAL_RCC_PWR_CLK_ENABLE();
    /*DBP : Enable access to Backup domain */
    HAL_PWR_EnableBkUpAccess();
    /*PWREN : Enable backup domain access  */
    /*BRE : Enable backup regulator
      BRR : Wait for backup regulator to stabilize */
    HAL_PWREx_EnableBkUpReg();
    /*DBP : Disable access to Backup domain */
    HAL_PWR_DisableBkUpAccess();
    read_from_backup_sram((uint8_t *)&retain_t, sizeof(retain_t), 4000);
    if (retain_t.v1 == retain_t.v2)
    {
        int v = memcmp(retain_t.sn, plc_curr_app->id, 32) == 0;
        return v;
    }
    return 0;
#endif
#endif
}
void hwboard::retain_validate()
{
    enRetain = 1;
}
void hwboard::retain_invalidate()
{
    enRetain = 0;
}

int setStepper(uint8_t ch,uint8_t run, unsigned int maxspeed, unsigned int acce, uint8_t pluse_por, uint8_t dir_por, int tar_abs, int tar_rel, uint8_t tar_set, int pos, uint8_t pos_set)
{
    if (ch > 0 && ch <= 10)
    {
        as[ch - 1].setMaxSpeed(maxspeed);
        as[ch - 1].setAcceleration(acce);
        as[ch - 1].setPinsInverted(dir_por, pluse_por);
        if (pos_set)
        {
            as[ch - 1].setCurrentPosition(pos);
        }
        tar_set=1;
        switch (tar_set)
        {
        case 1:
            as[ch - 1].moveTo(tar_abs);
            // as[ch - 1].runToPosition();
            break;
        case 2:
            as[ch - 1].move(tar_rel);
            // as[ch - 1].runToPosition();
        }
        as[ch-1].start(run);
    }
    return -1;
}
int getStepperPosition(uint8_t ch)
{
    if (ch > 0 && ch <= 10)
    {
        return as[ch - 1].currentPosition();
    }
    return -1;
}
int getStepperRunning(uint8_t ch)
{
    if (ch > 0 && ch <= 10)
    {
        return as[ch - 1].isRunning();
    }
    return -1;
}
int getStepperEnd(uint8_t ch)
{
    if (ch > 0 && ch <= 10)
    {
        return as[ch - 1].end();
    }
    return -1;
}